# sys.path.append())
import json

from core.constant import RangeType, BaseTypeList
from core.documents import APIModel, Ask, RawValue
from core.summarize_func import s_Enum, s_Range
from solvers import success_c


def build_args(data):
    """
    :param data: 是参数信息
    :return: 返回一个RawValue
    """
    res = RawValue()
    if isinstance(data, dict):
        # 字典类型
        res.val_type = 'dict'
        for key in data:
            res.dict_value[key] = build_args(data[key]).uid
    elif isinstance(data, list):
        # 列表类型
        res.val_type = 'list'
        for item in data:
            res.list_value.append(build_args(item).uid)
        res.value_range_type = RangeType.SP_LEN
    else:  # 基本类型
        for tp in BaseTypeList:
            if tp.check(data):
                res.val_type = str(tp())
                break
        res.value_range_type = RangeType.HISTORY
        res.value_range = json.dumps([data], ensure_ascii=False)
    res.uid = RawValue.get_last_uid()
    res.required = True
    res.available = False
    res.save()
    return res


def summarize(model, new_one):
    """
    通过以往的知识，进行总结
    :param model: 过去的知识
    :param new_one:  新的示例
    :return: None
    """
    if model.val_type != new_one.val_type:
        model.value_range_type = RangeType.CONFLICT  # 类型不一致，学到冲突范围，这是API不规范导致的
    if model.value_range_type in [RangeType.SQL_TABLE, RangeType.UNBOUND, RangeType, RangeType.CONFLICT]:
        # 这些参数不适合再做归纳
        return
    if model.val_type not in ['list', 'dict']:
        # 基本类型归纳
        if model.value_range_type in [RangeType.ENUM, RangeType.HISTORY]:
            model.value_range = s_Enum(model.value_range, new_one.value_range)
        elif model.value_range_type == RangeType.RANGE:
            model.value_range = s_Range(model.value_range, new_one.value_range, model.val_type)
    elif model.val_type == 'list':
        if len(model.list_value) != len(new_one.list_value):
            model.value_range_type = RangeType.VAR_LEN
            if len(model.list_value):
                model.list_value = [model.list_value[0]]
        # 对下面的条目进行归纳
        if model.value_range_type == RangeType.VAR_LEN:
            if model.list_value and new_one.list_value:
                summarize(RawValue.get_by_uid(model.list_value[0]),
                          RawValue.get_by_uid(new_one.list_value[0]))
                # 仅归纳第一个
            elif new_one.list_value:
                # 新增一个
                model.list_value.append(new_one.list_value[0])
        else:  # 数量固定
            for s, n in zip(map(RawValue.get_by_uid, model.list_value), map(RawValue.get_by_uid, new_one.list_value)):
                summarize(s, n)
    else:  # 对字典类型进行归纳
        for key in model.dict_value:
            item = RawValue.get_by_uid(model.dict_value[key])
            if key not in new_one.dict_value:
                item.required = False
            else:
                summarize(item, RawValue.get_by_uid(new_one.dict_value[key]))
            item.save()
        for key in new_one.dict_value:
            if key not in model.dict_value:
                item = RawValue.get_by_uid(new_one.dict_value[key])
                model.dict_value[key] = item.uid
                item.required = False
                item.save()
    model.save()


def mark_arg(arg):
    # 对所有的子类和本体打上有效标记
    arg.available = True
    arg.save()
    for item in arg.list_value:
        mark_arg(RawValue.get_by_uid(uid=item))
    for k in arg.dict_value:
        mark_arg(RawValue.get_by_uid(uid=arg.dict_value[k]))


def clean_args(md):
    mark_arg(md.args)
    mark_arg(md.result)
    for arg in RawValue.objects(available=False):
        RawValue.delete(arg)


def build_one_process(url):
    import time
    model = APIModel.objects(url=url).first()
    if not model:
        model = APIModel()
        model.url = url
        model.date = -1
    check_date = model.date
    # 第一步，通过元信息，筛选成功的API
    check_list = []
    model.has_file = False
    for ca in Ask.objects(url=url,date__gt=check_date):
        if success_c(ca):
            if ca.has_file:
                model.has_file = True
            check_list.append(ca)
    if model.has_file:  # 不为带文件的请求构建model
        raise StopIteration
    if not check_list:
        raise StopIteration # 没有合格的请求，无视这个url
    # 根据已有的信息，聚合信息生成最严格的model
    count = len(check_list)
    for i, ca in enumerate(check_list):
        if model.args is None:
            model.args = build_args(ca.data)
        else:  # 归纳
            summarize(model.args, build_args(ca.data))
        result = build_args(json.loads(ca.resp_content.decode()))
        if model.result is None:
            model.result = result
        else:
            summarize(model.result, result)
        model.method = ca.method
        yield int((i * 98) / count)
    clean_args(model)
    model.date = int(time.time())
    model.get_relate_url()
    model.save()
    yield 100


def build_one(url):
    return list(build_one_process(url))


def build():
    checked = set()
    for ask in Ask.objects():
        if ask.url in checked:
            continue
        checked.add(ask.url)
        print(ask.url)


if __name__ == '__main__':
    build()
